using System;
using System.Collections.Generic;
using System.Text;
using FlexminAPI;
using System.Diagnostics;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Threading;

namespace PowerManager
{
	public delegate void CancelDelegate();

	class ActionInfo
	{
		private int seconds;
		public int Seconds
		{
			get { return seconds; }
		}

		private PowerAction action;
		public PowerAction Action
		{
			get { return action; }
		}

		private string message;
		public string Message
		{
			get { return message; }
		}

		public ActionInfo(PowerAction action, int seconds, string message)
		{
			this.action = action;
			this.seconds = seconds;
			this.message = message;
		}

		public ActionInfo(PowerAction action, int seconds)
		{
			this.action = action;
			this.seconds = seconds;
			this.message = null;
		}
	}

	public class PowerSlave : ISlave
	{
		private bool AdjustPrivileges()
		{
			IntPtr hProcess = Process.GetCurrentProcess().Handle;
			IntPtr hToken;
			if (!WinApiWrapping.OpenProcessToken(hProcess, WinApiWrapping.TokenAccess.TOKEN_ADJUST_PRIVILEGES | WinApiWrapping.TokenAccess.TOKEN_QUERY, out hToken))
				return false;

			WinApiWrapping.LUID luid;
			if (!WinApiWrapping.LookupPrivilegeValue(null, "SeShutdownPrivilege", out luid))
			{
				WinApiWrapping.CloseHandle(hToken);
				return false;
			}

			WinApiWrapping.LUID_AND_ATTRIBUTES luidAndAttributes = new WinApiWrapping.LUID_AND_ATTRIBUTES();
			luidAndAttributes.Luid = luid;
			luidAndAttributes.Attributes = WinApiWrapping.Privilege.SE_PRIVILEGE_ENABLED;
			
			WinApiWrapping.TOKEN_PRIVILEGES tokenPrivileges = new WinApiWrapping.TOKEN_PRIVILEGES();
			tokenPrivileges.PrivilegeCount = 1;
			tokenPrivileges.Privileges = luidAndAttributes;
			
			WinApiWrapping.TOKEN_PRIVILEGES tokenPrivelegesPreviouse;
			int returnLength;

			if (!WinApiWrapping.AdjustTokenPrivileges(hToken, false, ref tokenPrivileges, Marshal.SizeOf(typeof(WinApiWrapping.TOKEN_PRIVILEGES)), out tokenPrivelegesPreviouse, out returnLength))
			{
				WinApiWrapping.CloseHandle(hToken);
				return false;
			}

			WinApiWrapping.CloseHandle(hToken);

			return true;
		}

		private bool HaltComputer()
		{
			if (AdjustPrivileges())
			{
				return WinApiWrapping.InitiateSystemShutdown(null, null, 0, false, false);
			}

			return false;
		}

		private bool HibernateComputer()
		{
			if (AdjustPrivileges())
			{
				return Application.SetSuspendState(PowerState.Hibernate, true, false);	
			}

			return false;
		}

		private bool SuspendComputer()
		{
			if (AdjustPrivileges())
			{
				return Application.SetSuspendState(PowerState.Suspend, true, false);
			}

			return false;
		}

		private bool RebootComputer()
		{
			if (AdjustPrivileges())
			{
				return WinApiWrapping.InitiateSystemShutdown(null, null, 0, false, true);
			}

			return false;
		}

		private bool LogoffUser()
		{
			if (AdjustPrivileges())
			{
				return WinApiWrapping.ExitWindowsEx(WinApiWrapping.ExitWindowsExFlags.EWX_LOGOFF | WinApiWrapping.ExitWindowsExFlags.EWX_FORCE,
													WinApiWrapping.ExitWindowsExReason.SHTDN_REASON_MAJOR_APPLICATION);
			}

			return false;
		}

		private bool die = false;
		private WarningForm wf;

		private void ShowWarning(object o)
		{
			ActionInfo info = o as ActionInfo;
			if (info == null)
				return;

			wf = new WarningForm(info.Action, info.Seconds);
			wf.ShowDialog();

			if (!die)
				PerformAction(info.Action);
		}

		public void PerformAction(PowerAction action)
		{
			switch (action)
			{
				case PowerAction.Halt:
					this.HaltComputer();
					break;
				case PowerAction.Reboot:
					this.RebootComputer();
					break;
				case PowerAction.Logoff:
					this.LogoffUser();
					break;
				case PowerAction.Suspend:
					this.SuspendComputer();
					break;
				case PowerAction.Hibernate:
					this.HibernateComputer();
					break;
			}
		}

		public void Cancel()
		{
			if (wf.InvokeRequired)
			{
				wf.Invoke(new CancelDelegate(Cancel));
				return;
			}

			die = wf.die = true;
			wf.Close();	
		}

		public void PerformAction(PowerAction action, int seconds)
		{
			Thread warnThread = new Thread(new ParameterizedThreadStart(ShowWarning));
			warnThread.Start(new ActionInfo(action, seconds));
		}

		public void PerformAction(PowerAction action, int seconds, string message)
		{
			Thread warnThread = new Thread(new ParameterizedThreadStart(ShowWarning));
			warnThread.Start(new ActionInfo(action, seconds, message));
		}
	}
}
